Karura sandwich attacks in the past 30 days

Row

Raw Data

block.id time attacker target profit currency
1763386 2022-04-14T22:33:36.609 t4PVii9P… qHrEVyEY… 5.101147 AUSD
1764563 2022-04-15T05:58:18.608 t4PVii9P… qXmxifJ5… 7.743719 AUSD
1775030 2022-04-18T00:46:42.231 t4PVii9P… qtRgxP95… 9.295307 AUSD
1775043 2022-04-18T00:51:48.525 t4PVii9P… qtRgxP95… 8.108215 AUSD
1779201 2022-04-19T03:02:48.275 t4PVii9P… sZLSbtg4… 9.258678 AUSD
1783201 2022-04-20T04:39:12.272 t4PVii9P… oYVonSHV… 56.564416 AUSD
1786130 2022-04-20T22:42:42.612 t4PVii9P… qtRgxP95… 8.588095 AUSD
1792992 2022-04-22T16:25:54.418 t4PVii9P… pyXki2UU… 5.627787 AUSD
1793642 2022-04-22T20:28:06.302 t4PVii9P… pyXki2UU… 13.875839 AUSD
1794246 2022-04-23T00:09:48.538 t4PVii9P… tWZQqSgj… 8.091017 AUSD
1795231 2022-04-23T06:12:30.493 t4PVii9P… qtRgxP95… 10.597481 AUSD
1796525 2022-04-23T14:02:12.421 t4PVii9P… qtRgxP95… 8.380745 AUSD
1797229 2022-04-23T18:17:42.39 t4PVii9P… rXBgtiMf… 14.244646 AUSD
1797302 2022-04-23T18:45:30.169 t4PVii9P… qtRgxP95… 6.856932 AUSD
1797618 2022-04-23T20:40:30.172 t4PVii9P… qtRgxP95… 8.444492 AUSD
1797951 2022-04-23T22:44:30.267 t4PVii9P… p3SKGrRH… 12.334130 AUSD
1799420 2022-04-24T07:32:42.22 t4PVii9P… tGzni538… 26.592624 AUSD
1799443 2022-04-24T07:40:54.357 t4PVii9P… qtRgxP95… 17.115859 AUSD
1799469 2022-04-24T07:49:42.529 t4PVii9P… qtRgxP95… 8.708309 AUSD
1799567 2022-04-24T08:23:12.195 t4PVii9P… qtRgxP95… 18.885492 AUSD
1799708 2022-04-24T09:16:00.243 t4PVii9P… tnq3xJ7L… 7.787859 AUSD
1800092 2022-04-24T11:36:18.285 t4PVii9P… qtRgxP95… 5.784155 AUSD
1801530 2022-04-24T20:19:18.571 t4PVii9P… qtRgxP95… 8.230302 AUSD
1801835 2022-04-24T22:17:24.514 t4PVii9P… qtRgxP95… 5.345694 AUSD
1802423 2022-04-25T01:55:18.652 t4PVii9P… rcGWYXGo… 5.641653 AUSD
1802775 2022-04-25T04:04:24.258 t4PVii9P… qHfZ4qC8… 9.395342 AUSD
1803067 2022-04-25T05:47:00.407 t4PVii9P… qXmxifJ5… 27.876923 AUSD
1803296 2022-04-25T07:06:30.338 t4PVii9P… pGTcwV7j… 37.777922 AUSD
1803318 2022-04-25T07:13:12.657 t4PVii9P… pGTcwV7j… 37.348302 AUSD
1805456 2022-04-25T20:07:42.358 t4PVii9P… rscV8fRt… 28.062067 AUSD
1805613 2022-04-25T21:03:12.277 t4PVii9P… pDubmCs2… 11.533055 AUSD
1807182 2022-04-26T06:38:12.411 t4PVii9P… ooDwe5pD… 5.741824 AUSD
1809508 2022-04-26T20:44:24.388 t4PVii9P… ocuC8zru… 5.602152 AUSD
1811767 2022-04-27T10:20:24.358 t4PVii9P… qtRgxP95… 8.164931 AUSD
1814659 2022-04-28T03:44:24.35 t4PVii9P… rnHVjvTT… 17.462503 AUSD
1816054 2022-04-28T12:05:42.562 t4PVii9P… oXgHePRW… 8.268756 AUSD
1816094 2022-04-28T12:19:30.379 t4PVii9P… oXgHePRW… 5.406004 AUSD
1816384 2022-04-28T14:06:18.541 t4PVii9P… pwt9PzWE… 7.321414 AUSD
1817713 2022-04-28T22:15:24.31 t4PVii9P… qHrEVyEY… 7.542795 AUSD
1828106 2022-05-01T12:08:48.209 t4PVii9P… pwt9PzWE… 17.644899 AUSD
1828188 2022-05-01T12:36:24.523 t4PVii9P… rXBgtiMf… 9.593515 AUSD
1829970 2022-05-01T22:59:02.706 t4PVii9P… qtRgxP95… 9.114611 AUSD
1833224 2022-05-02T18:50:42.616 tuxT4Xsi… tqLX4Syb… 4.950172 KAR
1834561 2022-05-03T03:02:42.335 t4PVii9P… pwt9PzWE… 6.198720 AUSD
1834952 2022-05-03T05:27:42.746 t4PVii9P… pwt9PzWE… 13.877820 AUSD
1840006 2022-05-04T12:03:12.325 t4PVii9P… oXgHePRW… 12.131904 AUSD
1840617 2022-05-04T15:45:18.534 t4PVii9P… sqRCxEaD… 15.210450 AUSD
1840923 2022-05-04T17:33:24.5 t4PVii9P… odukVAMP… 15.447788 AUSD
1846226 2022-05-06T00:31:06.412 t4PVii9P… raAdnAUJ… 10.731251 AUSD
1849005 2022-05-06T16:42:19.013 t4PVii9P… rUGMtr8o… 69.989384 AUSD
1855646 2022-05-08T06:52:48.227 t4PVii9P… u1BSDbT8… 11.676573 AUSD
1863150 2022-05-10T00:44:18.34 t4PVii9P… rcGWYXGo… 52.804447 AUSD
1864162 2022-05-10T06:05:50.711 t4PVii9P… sqRCxEaD… -18.391052 AUSD
1870025 2022-05-11T08:23:12.679 tuxT4Xsi… rh7zjB7E… 30.104430 KAR
1870452 2022-05-11T10:05:48.269 t4PVii9P… u1BSDbT8… 13.677420 AUSD
1873271 2022-05-11T21:18:42.633 t4PVii9P… rUGMtr8o… 13.072383 AUSD
1874950 2022-05-12T03:46:12.402 t4PVii9P… qJ6pUk4k… 7.999504 AUSD
1875986 2022-05-12T07:49:48.57 t4PVii9P… oQn2B1LK… -14.995782 AUSD
1875996 2022-05-12T07:51:54.522 t4PVii9P… rWsv8gne… 10.847079 AUSD
1878762 2022-05-12T18:37:06.604 t4PVii9P… tZfhx6NZ… 10.735460 AUSD

Row

Sources:

Last updated: 2022-05-13 09:35:21

Date range of data: 2022-04-13T00:18:54.324 to 2022-05-12T23:52:12.263.

Sources:

Swaps:

---
title: "Acala / Karura Sandwich Attack Dashboards"
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    vertical_layout: scroll
    social: menu
    source_code: embed
params:
  network: Karura
  window: 30
---

```{css custom1, echo=FALSE}
.dataTables_scrollBody {
    max-height: 100% !important;
}
```

```{r global, include=FALSE}

knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  comment = "#>"
)

library(kableExtra)
library(formattable)
library(lubridate)
library(flexdashboard)
library(DT)
library(dygraphs)

# Helper function to concat
`%+%` <- function(a, b) paste0(a, b)



# remotes::install_github("ropensci/ghql") # if package is not already installed
library(jsonlite)
library(data.table)
library(ghql)
x <- GraphqlClient$new()


window <- params$window
endpoint <- params$endpoint
network <- params$network

if (network == 'Acala') {
  
  dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala-dex"
  loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-loan-subql"
  swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-swap-day-data"
  official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala"
  
} else {

  dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura-dex"
  loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-loan-subql"
  swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-test"
  official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura"

}

getSwapsDex <- function(endpoint, window) {
  # endpoint <- dex_endpoint; window <- 10
  
  # make a client
  cli <- GraphqlClient$new(url = endpoint)
  mindate <- today(tzone = 'UTC') - window
  
  cursor <- ''
  resList <- list()
  for (i in 1:1000) {
    if (cursor == '') {
      cursorStr <- 'first:100'
    } else {
      cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
    }
    qry <- Query$new()
    qry$query('swaps', '
    {
      query {
        swaps (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
          totalCount
          edges {
            node { 
                id address {
                  id
                } pool {
                  id
                }  token0 {
                  id
                } token1 {
                  id
                } token0InAmount token1OutAmount
							  tradePath price0 price1 block {
							    id
							  } extrinsic {
							    id
							  } timestamp
            }
            cursor
          }
          pageInfo {
            endCursor
            hasNextPage
          }
        }
      }
    }')
    result <- cli$exec(qry$queries$swaps)  %>%
      fromJSON(flatten=TRUE)
    cursor <- result$data$query$swaps$pageInfo$endCursor
    res <- as.data.table(result$data$query$swaps$edges)
    res[, cursor := NULL]
    
    print(i %+% " " %+% nrow(res))
    resList[[i]] <- res
    if (result$data$query$swaps$pageInfo$hasNextPage == FALSE) break
  }
  res <- rbindlist(resList)
  setnames(res, old = names(res), new = gsub("node.", "", names(res)))
  
  if (substr(max(res$timestamp), 12, 13) < 23) {
    maxdate <- as.Date(max(res$timestamp))-1
  } else {
    maxdate <- as.Date(max(res$timestamp))
  }
  
  res <- res[timestamp <= maxdate]
  res[, date := as.Date(timestamp)]
  setorder(res, timestamp)
  
  # Replace foreign assets
  res[, token0.id := subscanr::fixToken(token0.id)]
  res[, token1.id := subscanr::fixToken(token1.id)]

  # Normalize pairs
  res[, pair := paste0(token0.id %+% ":" %+% token1.id)]
  res[token1.id < token0.id, pair := paste0(token1.id %+% ":" %+% token0.id)]
  res[, exclude := token0.id == token1.id]
  res  
  
}


# queries ####
# swaps2 <- getDailyPoolsQuery(dex_endpoint, window)
swaps <- getSwapsDex(dex_endpoint, window)

swaps <- merge(swaps, subscanr::tokens, by.x='token0.id', by.y='Token') %>% setnames("decimals","decimals0")
swaps[, Name := NULL]
swaps <- merge(swaps, subscanr::tokens, by.x='token1.id', by.y='Token') %>% setnames("decimals","decimals1")
swaps[, Name := NULL]

swaps[, adj0 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals0) + 1))]
swaps[, adj1 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals1) + 1))]

swaps[, token0InAmount := as.numeric(token0InAmount)]
swaps[, token1OutAmount := as.numeric(token1OutAmount)]
swaps[, price0 := as.numeric(price0) / 1e18]
swaps[, price1 := as.numeric(price1) / 1e18]

swaps[, amount0 := token0InAmount / adj0]
swaps[, amount1 := token1OutAmount / adj1]

swaps[, token0.id := subscanr::fixToken(token0.id)]
swaps[, token1.id := subscanr::fixToken(token1.id)]
swaps[, tradePath := subscanr::fixToken(tradePath)]
swaps[, pathLength := length(strsplit(tradePath, ",")[[1]]) - 1, by = id]

swaps[, volume0USD := amount0 * price0]
swaps[, volume1USD := amount1 * price1]
swaps[, volumeUSDFloat := (volume0USD + volume1USD) / 2]
swaps[volume0USD == 0 & volume1USD > 0, volumeUSDFloat := volume1USD]
swaps[volume1USD == 0 & volume0USD > 0, volumeUSDFloat := volume0USD]

mysort <- function(a, b) ifelse(a < b, a %+% ":" %+% b, b %+% ":" %+% a)

getPath <- function(tradePath) {
  # tradePath <- swaps[1]$tradePath
  tp <- strsplit(tradePath, ",")[[1]]
  n <- length(tp) - 1
  if (n == 3) {
    return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), mysort(tp[3],tp[4])))
  } else if (n == 2) {
    return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), "NA:NA"))
  } 
  list(mysort(tp[1],tp[2]), "NA:NA", "NA:NA")
}
swaps[, c("pair1", "pair2", "pair3") := getPath(tradePath), by = id]

swaps[, fee := volumeUSDFloat * .03]
swaps[, feeAdj := volumeUSDFloat * .03 * pathLength]
setnames(swaps, "address.id", "accountId")


index <- strsplit(swaps$id, "-")
index <- do.call("rbind", index)
myindex <- as.numeric(index[, 2])
swaps[, index := myindex]
setorder(swaps, block.id, index)

starti <- min(swaps$block.id) #
endi <- max(swaps$block.id)


j <- 1
d <- list()
prof <- list()
for (i in starti:endi) {
  # i <- 1829970
  tmp <- swaps[block.id == i]
  tmpn <- tmp[, .N, by = 'accountId']

  # Look for the same account number 2 times in the same block
  if (nrow(tmpn) >= 2 && max(tmpn$N) >= 2) {
    acct <- tmp[, .N, by = 'accountId'][N >= 2, accountId]
    for (account in acct) {
      # account <- acct[1]
      for (ii in 1:(nrow(tmp) - 2)) {
      
          if (tmp$token0.id[ii] == tmp$token1.id[ii+2] && 
              tmp$token1.id[ii] == tmp$token0.id[ii+2] && 
              tmp$accountId[ii] == tmp$accountId[[ii+2]] &&
              tmp$accountId[ii] != tmp$accountId[[ii+1]] &&
              length(grep(tmp$token0.id[ii], tmp$tradePath[ii+1])) > 0 && 
              length(grep(tmp$token1.id[ii], tmp$tradePath[ii+1])) > 0) {
            d[[j]] <- tmp[, .(block.id, index, token0.id, token1.id, tradePath, token0InAmount, token1OutAmount, price0, price1, timestamp, accountId)][ii:(ii+2)] 
            prof[[j]] = data.table(block.id = tmp$block.id[ii], time = tmp$timestamp[ii], attacker = tmp$accountId[ii], target = tmp$accountId[ii+1], profit = as.numeric(tmp$token1OutAmount[ii+2]) - as.numeric(tmp$token0InAmount[ii]), currency = tmp$token0.id[ii])
            j <- j + 1  
          }
      } # end for ii
    } # end for account
  }
}
out <- rbindlist(d)
p <- rbindlist(prof)
output <- unique(p)
output[, profit := profit / 1e12]
output[, attacker := substr(attacker, 1, 8) %+% "..."]
output[, target := substr(target, 1, 8) %+% "..."]

```

### `r network` sandwich attacks in the past 30 days

```{r plot}

dygraph(output[, .N, by = as.Date(time)], main = network %+% " Sandwich Attacks") %>% 
        dyStackedBarChart()

```

Row
---

### Raw Data

```{r table}

knitr::kable(output, escape = FALSE) %>%
  kable_styling()

```

Row
---

### Sources:

Last updated: `r Sys.time()`

Date range of data: `r min(swaps$timestamp)` to `r max(swaps$timestamp)`.

Sources: 

* [SubQuery Network](https://explorer.subquery.network/)

Swaps:

* [Acala-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/acala-swap-day-data)

* [Karura-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/karura-swap-day-data)